/**
 * PPH系统Web管理界面服务器
 * 提供服务管理、日志查看、状态监控等功能
 */

const express = require('express');
const cors = require('cors');
const path = require('path');
const fs = require('fs');
const { exec, spawn } = require('child_process');
const util = require('util');

const app = express();
const PORT = process.env.MANAGEMENT_PORT || 3012;
const execAsync = util.promisify(exec);

// 在 Windows 下稳妥解析 PM2 可执行文件路径，避免 PATH 缺失导致的失败或弹窗
const PM2_CMD = (() => {
  try {
    if (process.platform === 'win32') {
      const appData = process.env.APPDATA; // e.g., C:\Users\<User>\AppData\Roaming
      if (appData) {
        const candidate = path.join(appData, 'npm', 'pm2.cmd');
        if (fs.existsSync(candidate)) return `"${candidate}"`;
      }
      // 退回尝试直接用 pm2（若 PATH 已配置）
      return 'pm2';
    }
    return 'pm2';
  } catch {
    return 'pm2';
  }
})();

// 中间件配置
app.use(cors());
app.use(express.json());
app.use(express.static(path.join(__dirname, 'public')));

// 日志文件路径
const LOG_DIR = path.join(__dirname, '../app/logs');
const DEPLOY_DIR = path.join(__dirname, '..');
const ECO_CONFIG = path.join(DEPLOY_DIR, 'config', 'ecosystem.config.cjs');

// 轻量任务队列（内存）
const JOBS = new Map();
let JOB_SEQ = 1;

// 确保日志目录存在
if (!fs.existsSync(LOG_DIR)) {
  fs.mkdirSync(LOG_DIR, { recursive: true });
}

// 避免 favicon 404
app.get('/favicon.ico', (req, res) => res.status(204).end());

// 日志清洗与审计写入
function stripAnsi(s){
  try{ return String(s||'').replace(/\x1B\[[0-?]*[ -\/]*[@-~]/g, ''); }catch{ return String(s||''); }
}
function cleanText(s){
  try{ return stripAnsi(s).replace(/[\r\n]+/g,' ').replace(/[\t]+/g,' ').trim(); }catch{ return ''; }
}
function writeAudit(ip, action, service, ok, err){
  try{
    const line = `${new Date().toISOString()}\t${cleanText(ip||'-')}\t${cleanText(action)}\t${cleanText(service)}\t${ok?1:0}\t${cleanText(err)}\n`;
    fs.appendFile(path.join(LOG_DIR, 'management-audit.log'), line, ()=>{});
  }catch(e){ /* 忽略审计写入失败 */ }
}

// 统一清洗与 JSON 解析，增强兼容性（BOM/ANSI/杂质）
function sanitizeJsonLike(s){
  try{
    let t = String(s || '');
    // 去除 BOM
    t = t.replace(/\uFEFF/g, '');
    // 去除 ANSI 颜色
    t = t.replace(/\x1B\[[0-9;]*m/g, '');
    // 去除首尾空白
    t = t.trim();
    // 若前面混入了日志/提示，截到第一个 JSON 起始符
    const start = t.search(/[\[\{]/);
    if(start > 0) t = t.slice(start);
    // 截到最后一个 JSON 结束符，避免尾部提示
    const end = Math.max(t.lastIndexOf(']'), t.lastIndexOf('}'));
    if(end >= 0) t = t.slice(0, end + 1);
    return t;
  }catch{
    return '';
  }
}

function safeJsonParse(s){
  try{
    return JSON.parse(typeof s === 'string' ? s : String(s || ''));
  }catch(_e1){
    const cleaned = sanitizeJsonLike(s);
    try{
      return JSON.parse(cleaned || '[]');
    }catch(e2){
      console.error('[management] JSON parse failed', { message: e2 && e2.message, sample: String(s || '').slice(0, 120) });
      throw e2;
    }
  }
}


// 安全限制：仅允许白名单命令
const PM2_SAFE_WHITELIST = new Set(['jlist','start','stop','restart','delete','flush','reload','logs','list','startup','save']);
function isSafePM2Command(cmd){
  try{
    const first = String(cmd).trim().split(/\s+/)[0];
    return PM2_SAFE_WHITELIST.has(first);
  }catch{ return false; }
}

// 执行PM2命令的辅助函数
async function executePM2Command(command) {
  try {
    const safeCheck = (typeof isSafePM2Command === 'function')
      ? isSafePM2Command
      : (cmd) => { try { const first = String(cmd).trim().split(/\s+/)[0]; return PM2_SAFE_WHITELIST.has(first); } catch { return false; } };

    if(!safeCheck(command)){
      return { success:false, error:'Forbidden PM2 command' };
    }
    const { stdout, stderr } = await execAsync(`${PM2_CMD} ${command}` , { windowsHide: true });
    // 清理ANSI颜色代码，防止JSON解析错误
    const cleanOutput = stdout.replace(/\x1b\[[0-9;]*m/g, '');
    return { success: true, output: cleanOutput, error: stderr };
  } catch (error) {
    return { success: false, error: error.message, output: error.stdout };
  }
}

/**
 * 获取服务状态
 */
app.get('/api/services/status', async (req, res) => {
  try {
    const result = await executePM2Command('jlist --no-color');
    if (result.success) {
      const processes = safeJsonParse(result.output || '[]');
      const pphProcesses = processes.filter(p =>
        p.name && p.name.startsWith('pph-')
      );

      res.json({
        success: true,
        services: pphProcesses.map(p => {
          const monit = p && p.monit || {};
          const env = p && p.pm2_env || {};
          return {
            name: p.name,
            status: env.status,
            pid: p.pid,
            uptime: env.pm_uptime,
            restarts: env.restart_time,
            memory: typeof monit.memory === 'number' ? monit.memory : 0,
            cpu: typeof monit.cpu === 'number' ? monit.cpu : 0
          };
        })
      });
    } else {
      res.status(500).json({ success: false, error: result.error });
    }
  } catch (error) {
    res.status(500).json({ success: false, error: error.message });
  }
});

/**
 * 启动服务
 */
app.post('/api/services/start', async (req, res) => {
  try {
    const { service } = req.body;

    if (service === 'all') {
      // 只启动前端与后端，管理端保持在线
      const configPath = path.join(DEPLOY_DIR, 'config/ecosystem.config.cjs');
      const r1 = await executePM2Command(`start "${configPath}" --only pph-frontend --env production`);
      const r2 = await executePM2Command(`start "${configPath}" --only pph-backend --env production`);
      const result = { success: !!(r1.success && r2.success), results: { frontend: r1, backend: r2 } };
      res.json(result);
    } else {
      // 针对常用别名补全
      if (service === 'frontend' || service === 'pph-frontend') {
        const result = await executePM2Command(`start "${ECO_CONFIG}" --only pph-frontend --env production`);
        return res.json(result);
      }
      if (service === 'backend' || service === 'pph-backend') {
        const result = await executePM2Command(`start "${ECO_CONFIG}" --only pph-backend --env production`);
        return res.json(result);
      }
      const result = await executePM2Command(`start ${service}`);
      res.json(result);
    }
  } catch (error) {
    res.status(500).json({ success: false, error: error.message });
  }
});

/**
 * 停止服务
 */
app.post('/api/services/stop', async (req, res) => {
  try {
    const { service } = req.body;

    // 禁止通过API停止管理端自身，避免 UI 失联
    if (service === 'pph-management' || service === 'management') {
      writeAudit(req.ip, 'stop', String(service), false, 'Forbidden to stop management via API');
      return res.status(400).json({ success:false, error:'Forbidden to stop management via API' });
    }

    if (service === 'all') {
      // 只停止前端与后端，避免把管理端自身停掉导致前端报 Failed to fetch
      const r1 = await executePM2Command('stop pph-frontend');
      const r2 = await executePM2Command('stop pph-backend');
      return res.json({ success: !!(r1.success && r2.success), results: { frontend: r1, backend: r2 } });
    } else if (service === 'frontend' || service === 'pph-frontend') {
      const r = await executePM2Command('stop pph-frontend');
      return res.json(r);
    } else if (service === 'backend' || service === 'pph-backend') {
      const r = await executePM2Command('stop pph-backend');
      return res.json(r);
    } else {
      const result = await executePM2Command(`stop ${service}`);
      return res.json(result);
    }
  } catch (error) {
    res.status(500).json({ success: false, error: error.message });
  }
});

/**
 * 异步队列：提交服务操作任务
 */
app.post('/api/services/queue', async (req, res) => {
  try{
    const { action, service } = req.body || {};
    if(!['start','stop','restart'].includes(action) || !service){
      return res.status(400).json({ success:false, error:'invalid params' });
    }
    const id = String(JOB_SEQ++);
    JOBS.set(id, { id, status:'queued', createdAt:Date.now() });
    // 立即异步执行
    setImmediate(async () => {
      const job = JOBS.get(id);
      if(!job) return;
      job.status = 'running';
      try{
        // 针对常用服务名提供智能顺序执行（失败后再尝试生态文件启动）
        const svc = String(service||'').toLowerCase();
        const act = String(action||'').toLowerCase();

        async function tryRestartOrStart(){
          // 禁止通过队列停止/重启管理端自身，避免 UI 断连
          if(svc === 'pph-management' || svc === 'management'){
            if(act === 'stop' || act === 'restart'){
              return { success:false, error:'Forbidden to stop/restart management via queue' };
            }
            if(act === 'start'){
              return { success:true, output:'noop' };
            }
          }

          // 处理 all：仅操作前端/后端，避免停止管理端自身
          if(svc === 'all'){
            if(act === 'stop'){
              const r1 = await executePM2Command('stop pph-frontend');
              const r2 = await executePM2Command('stop pph-backend');
              return { success: !!(r1.success && r2.success), results:{ frontend:r1, backend:r2 } };
            }
            if(act === 'start'){
              const r1 = await executePM2Command(`start "${ECO_CONFIG}" --only pph-frontend --env production`);
              const r2 = await executePM2Command(`start "${ECO_CONFIG}" --only pph-backend --env production`);
              return { success: !!(r1.success && r2.success), results:{ frontend:r1, backend:r2 } };
            }
            if(act === 'restart'){
              let r1 = await executePM2Command('restart pph-frontend');
              if(!r1.success){ r1 = await executePM2Command(`start "${ECO_CONFIG}" --only pph-frontend --env production`); }
              let r2 = await executePM2Command('restart pph-backend');
              if(!r2.success){ r2 = await executePM2Command(`start "${ECO_CONFIG}" --only pph-backend --env production`); }
              return { success: !!(r1.success && r2.success), results:{ frontend:r1, backend:r2 } };
            }
          }

          if(act === 'restart'){
            if(svc === 'pph-frontend' || svc === 'frontend'){
              let r = await executePM2Command('restart pph-frontend');
              if(!r.success) r = await executePM2Command(`start "${ECO_CONFIG}" --only pph-frontend --env production`);
              return r;
            }
            if(svc === 'pph-backend' || svc === 'backend'){
              let r = await executePM2Command('restart pph-backend');
              if(!r.success) r = await executePM2Command(`start "${ECO_CONFIG}" --only pph-backend --env production`);
              return r;
            }
            return await executePM2Command(`restart ${service}`);
          } else if (act === 'start'){
            if(svc === 'pph-frontend' || svc === 'frontend'){
              return await executePM2Command(`start "${ECO_CONFIG}" --only pph-frontend --env production`);
            }
            if(svc === 'pph-backend' || svc === 'backend'){
              return await executePM2Command(`start "${ECO_CONFIG}" --only pph-backend --env production`);
            }
            return await executePM2Command(`start ${service}`);
          } else if (act === 'stop'){
            if(svc === 'pph-frontend' || svc === 'frontend') return await executePM2Command('stop pph-frontend');
            if(svc === 'pph-backend' || svc === 'backend') return await executePM2Command('stop pph-backend');
            return await executePM2Command(`stop ${service}`);
          }
          return { success:false, error: 'Unsupported action' };
        }

        const result = await tryRestartOrStart();
        const ok = !!result.success;
        job.status = ok ? 'success':'failed';
        job.result = result;
        // 审计
        writeAudit(req.ip, action, service, ok, result.error || result.output);
      }catch(e){
        job.status = 'failed';
        job.error = e && (e.message||String(e));
        writeAudit(req.ip, action, service, false, job.error);
      }
      // 10分钟后自动清理
      setTimeout(()=>{ JOBS.delete(id); }, 10*60*1000);
    });
    return res.json({ success:true, jobId:id });
  }catch(error){
    res.status(500).json({ success:false, error: error.message });
  }
});

// 任务状态
app.get('/api/jobs/status/:id', (req,res)=>{
  try{
    const job = JOBS.get(req.params.id);
    if(!job){ return res.json({ success:true, job:{ id:req.params.id, status:'not_found' } }); }
    return res.json({ success:true, job });
  }catch(error){
    res.status(500).json({ success:false, error:error.message });
  }
});

/**
 * 审计列表
 */
app.get('/api/audit/list', async (req,res)=>{
  try{
    const limit = Math.max(1, Math.min(500, parseInt(req.query.limit||'100')));
    const offset = Math.max(0, parseInt(req.query.offset||'0'));
    const action = String(req.query.action||'').trim();
    const service = String(req.query.service||'').trim();
    const success = String(req.query.success||'').trim();
    const file = path.join(LOG_DIR, 'management-audit.log');
    if(!fs.existsSync(file)) return res.json({ success:true, items:[], total:0 });
    const raw = await fs.promises.readFile(file,'utf8');
    const lines = raw.split(/\r?\n/).filter(Boolean);
    // 解析列：ISO时间\tIP\t动作\t服务\t1/0\t错误
    let rows = lines.map(l=>{
      const [ts, ip, act, svc, ok, err] = l.split('\t');
      return { ts, ip, action: act, service: svc, success: ok==='1', error: err };
    });
    if(action) rows = rows.filter(r=>r.action===action);
    if(service) rows = rows.filter(r=>r.service===service);
    if(success==='1'||success==='0') rows = rows.filter(r=>r.success === (success==='1'));
    const total = rows.length;
    rows = rows.slice(offset, offset+limit);
    return res.json({ success:true, items: rows, total });
  }catch(error){
    res.status(500).json({ success:false, error:error.message });
  }
});

/**
 * 重启服务
 */

/**
 * pm2-logrotate 状态
 */
app.get('/api/services/logrotate/status', async (req, res) => {
  try{
    const result = await executePM2Command('jlist');
    if(!result.success) return res.status(500).json(result);
    const processes = safeJsonParse(result.output || '[]');
    const rotate = processes.find(p => p.name === 'pm2-logrotate');
    res.json({ success:true, installed: !!rotate, status: rotate?.pm2_env?.status || 'unknown' });
  }catch(error){
    res.status(500).json({ success:false, error: error.message });
  }
});

/**
 * 批量操作（start/stop/restart）+ 审计日志
 */
app.post('/api/services/batch', async (req, res) => {
  try{
    const { action, services = [] } = req.body || {};
    if(!['start','stop','restart'].includes(action)){
      return res.status(400).json({ success:false, error:'invalid action' });
    }
    if(!Array.isArray(services) || services.length===0){
      return res.status(400).json({ success:false, error:'empty services' });
    }
    const results = [];
    for(const s of services){
      const r = await executePM2Command(`${action} ${s}`);
      results.push({ service: s, ...r });
    }
    // 审计日志（逐条记录，便于过滤）
    for(const s of services){ writeAudit(req.ip, `batch-${action}`, s, true, ''); }
    res.json({ success:true, results });
  }catch(error){
    res.status(500).json({ success:false, error: error.message });
  }
});

app.post('/api/services/restart', async (req, res) => {
  try {
    const { service } = req.body;

    if (service === 'all') {
      // 只重启前端与后端（保持管理端在线）
      let r1 = await executePM2Command('restart pph-frontend');
      if(!r1.success){ r1 = await executePM2Command(`start "${ECO_CONFIG}" --only pph-frontend --env production`); }
      let r2 = await executePM2Command('restart pph-backend');
      if(!r2.success){ r2 = await executePM2Command(`start "${ECO_CONFIG}" --only pph-backend --env production`); }
      return res.json({ success: !!(r1.success && r2.success), results: { frontend: r1, backend: r2 } });
    } else if (service === 'frontend' || service === 'pph-frontend') {
      const result = await executePM2Command(`restart pph-frontend || start "${ECO_CONFIG}" --only pph-frontend --env production`);
      return res.json(result);
    } else if (service === 'backend' || service === 'pph-backend') {
      const result = await executePM2Command(`restart pph-backend || start "${ECO_CONFIG}" --only pph-backend --env production`);
      return res.json(result);
    } else {
      const result = await executePM2Command(`restart ${service}`);
      return res.json(result);
    }
  } catch (error) {
    res.status(500).json({ success: false, error: error.message });
  }
});

/**
 * 获取日志列表
 */
app.get('/api/logs/list', (req, res) => {
  try {
    const logFiles = fs.readdirSync(LOG_DIR)
      .filter(file => file.endsWith('.log'))
      .map(file => {
        const filePath = path.join(LOG_DIR, file);
        const stats = fs.statSync(filePath);
        return {
          name: file,
          size: stats.size,
          modified: stats.mtime,
          path: filePath
        };
      })
      .sort((a, b) => b.modified - a.modified);

    res.json({ success: true, logs: logFiles });
  } catch (error) {
    res.status(500).json({ success: false, error: error.message });
  }
});

/**
 * 获取日志内容（纯 Node 实现，避免启动 PowerShell 弹窗）
 */
app.get('/api/logs/content/:filename', async (req, res) => {
  try {
    const { filename } = req.params;
    const lines = Number(req.query.lines || 100);
    const filePath = path.join(LOG_DIR, filename);

    if (!fs.existsSync(filePath)) {
      return res.status(404).json({ success: false, error: 'Log file not found' });
    }

    const data = await fs.promises.readFile(filePath, 'utf8');
    const arr = data.split(/\r?\n/);
    const tail = arr.slice(Math.max(arr.length - lines, 0)).join('\n');

    res.json({ success: true, content: tail, filename, lines });
  } catch (error) {
    res.status(500).json({ success: false, error: error.message });
  }
});

/**
 * 实时日志流（纯 Node 实现，无 PowerShell 弹窗）
 */
app.get('/api/logs/stream/:filename', (req, res) => {
  const { filename } = req.params;
  const filePath = path.join(LOG_DIR, filename);

  if (!fs.existsSync(filePath)) {
    return res.status(404).json({ success: false, error: 'Log file not found' });
  }

  res.writeHead(200, {
    'Content-Type': 'text/event-stream',
    'Cache-Control': 'no-cache',
    'Connection': 'keep-alive',
    'Access-Control-Allow-Origin': '*'
  });

  try {
    // 回放最近10行
    const data = fs.readFileSync(filePath, 'utf8');
    const arr = data.split(/\r?\n/);
    const initTail = arr.slice(Math.max(arr.length - 10, 0)).join('\n');
    if (initTail) res.write(`data: ${initTail}\n\n`);
  } catch (e) {
    res.write(`data: ERROR: ${e.message}\n\n`);
  }

  let lastSize = 0;
  try {
    lastSize = fs.statSync(filePath).size;
  } catch (e) {
    lastSize = 0;
  }

  const watcher = fs.watch(filePath, { persistent: true }, () => {
    try {
      const stats = fs.statSync(filePath);
      if (stats.size < lastSize) {
        // 文件被截断/轮转
        lastSize = 0;
      }
      const stream = fs.createReadStream(filePath, { start: lastSize, end: stats.size });
      stream.on('data', (chunk) => {
        res.write(`data: ${chunk.toString()}\n\n`);
      });
      stream.on('end', () => {
        lastSize = stats.size;
      });
      stream.on('error', (err) => {
        res.write(`data: ERROR: ${err.message}\n\n`);
      });
    } catch (err) {
      res.write(`data: ERROR: ${err.message}\n\n`);
    }
  });

  req.on('close', () => {
    try { watcher.close(); } catch {}
  });
});

/**
 * 导出日志
 */
app.get('/api/logs/export/:filename', (req, res) => {
  try {
    const { filename } = req.params;
    const filePath = path.join(LOG_DIR, filename);

    if (!fs.existsSync(filePath)) {
      return res.status(404).json({ success: false, error: 'Log file not found' });
    }

    res.download(filePath, filename);
  } catch (error) {
    res.status(500).json({ success: false, error: error.message });
  }
});

/**
 * 获取系统信息
 */
app.get('/api/system/info', async (req, res) => {
  try {
    const os = require('os');

    // 获取端口状态
    const portCheck = async (port) => {
      try {
        const { stdout } = await execAsync(`netstat -an | findstr :${port} | findstr LISTENING`, { windowsHide: true });
        return stdout.trim().length > 0;
      } catch {
        return false;
      }
    };

    // 计算系统开机时间与运行秒数（Windows 使用 wmic 作为更准确来源，其他平台用 os.uptime）
    async function getSystemUptimeSeconds(){
      try{
        if (process.platform === 'win32'){
          try{
            const { stdout } = await execAsync('wmic os get lastbootuptime /value', { windowsHide: true });
            const m = /lastbootuptime=([0-9]{14})/i.exec(stdout.replace(/\r?\n/g,''));
            if(m && m[1]){
              const s = m[1];
              // yyyymmddHHMMSS -> Date
              const dt = new Date(Date.UTC(
                parseInt(s.slice(0,4)), parseInt(s.slice(4,6))-1, parseInt(s.slice(6,8)),
                parseInt(s.slice(8,10)), parseInt(s.slice(10,12)), parseInt(s.slice(12,14))
              ));
              const bootMs = dt.getTime();
              const nowMs = Date.now();
              if(nowMs > bootMs){ return Math.floor((nowMs - bootMs)/1000); }
            }
          }catch{ /* fallback below */ }
        }
        const sec = os.uptime();
        return Math.max(0, Math.floor(sec));
      }catch{ return Math.max(0, Math.floor(os.uptime()||0)); }
    }

    // 读取管理端进程的 PM2 启动时间，返回进程运行毫秒
    async function getManagementProcessUptimeMs(){
      try{
        const r = await executePM2Command('jlist');
        if(!r.success) return null;
        const list = safeJsonParse(r.output||'[]');
        const proc = Array.isArray(list) ? list.find(p=>p && p.name==='pph-management') : null;
        const startedAt = proc && proc.pm2_env && typeof proc.pm2_env.pm_uptime === 'number' ? proc.pm2_env.pm_uptime : null;
        if(typeof startedAt === 'number' && startedAt>0){ return Date.now() - startedAt; }
        return null;
      }catch{ return null; }
    }

    const uptimeSeconds = await getSystemUptimeSeconds();
    const mgmtUptimeMs = await getManagementProcessUptimeMs();

    const systemInfo = {
      hostname: os.hostname(),
      platform: os.platform(),
      arch: os.arch(),
      uptime: uptimeSeconds,
      memory: {
        total: os.totalmem(),
        free: os.freemem(),
        used: os.totalmem() - os.freemem()
      },
      cpu: os.cpus()[0],
      mgmtProcessUptimeMs: mgmtUptimeMs,
      ports: {
        frontend: await portCheck(3010),
        backend: await portCheck(3011),
        management: await portCheck(3012)
      },
      timestamp: new Date().toISOString()
    };

    res.json({ success: true, system: systemInfo });
  } catch (error) {
    res.status(500).json({ success: false, error: error.message });
  }
});

/**
 * 系统操作：PM2 开机自启 + 保存
 * 安全：仅本机可调用（来自 127.0.0.1），且带 try/catch 审计
 */
app.post('/api/system/pm2/startup-save', async (req, res) => {
  try {
    const ip = req.ip || '';
    if (!/^::1|127\.0\.0\.1/.test(ip)) {
      writeAudit(ip, 'pm2-startup-save', '-', false, 'forbidden: only localhost');
      return res.status(403).json({ success: false, error: 'forbidden' });
    }
    const r1 = await executePM2Command('startup windows');
    const r2 = await executePM2Command('save');
    const ok = !!(r1.success && r2.success);
    writeAudit(ip, 'pm2-startup-save', '-', ok, (r1.error||'') + ' ' + (r2.error||''));
    return res.json({ success: ok, results: { startup: r1, save: r2 } });
  } catch (error) {
    writeAudit(req.ip, 'pm2-startup-save', '-', false, error.message);
    res.status(500).json({ success: false, error: error.message });
  }
});

/**
 * 系统操作：开放防火墙端口 3010/3011/3012
 * 注意：需要管理端以管理员权限运行，否则命令会失败
 */
app.post('/api/system/firewall/open', async (req, res) => {
  try {
    const ip = req.ip || '';
    if (!/^::1|127\.0\.0\.1/.test(ip)) {
      writeAudit(ip, 'firewall-open', '-', false, 'forbidden: only localhost');
      return res.status(403).json({ success: false, error: 'forbidden' });
    }
    const rules = [
      'netsh advfirewall firewall add rule name="PPH-Frontend-3010" dir=in action=allow protocol=TCP localport=3010',
      'netsh advfirewall firewall add rule name="PPH-Backend-3011" dir=in action=allow protocol=TCP localport=3011',
      'netsh advfirewall firewall add rule name="PPH-Management-3012" dir=in action=allow protocol=TCP localport=3012'
    ];
    const results = [];
    for (const cmd of rules) {
      try {
        const { stdout } = await execAsync(cmd, { windowsHide: true });
        results.push({ cmd, success: true, output: stdout });
      } catch (e) {
        results.push({ cmd, success: false, error: e.message });
      }
    }
    const ok = results.every(r => r.success);
    writeAudit(ip, 'firewall-open', '-', ok, ok ? '' : JSON.stringify(results.filter(r=>!r.success)));
    return res.json({ success: ok, results });
  } catch (error) {
    writeAudit(req.ip, 'firewall-open', '-', false, error.message);
    res.status(500).json({ success: false, error: error.message });
  }
});



// 第三方连通性探测：转调后端代理，5秒超时
app.get('/api/third-party/dsp/ping', async (req, res) => {
  const controller = new AbortController();
  const timer = setTimeout(()=>controller.abort(), 5000);
  try{
    const base = (process.env.BACKEND_API_BASE || 'http://127.0.0.1:3011');
    const url = `${base}/api/proxy/lenovo-hid?path=/lenovo/bid&clientType=api&positionId=1`;
    const r = await fetch(url, { method:'GET', signal: controller.signal });
    clearTimeout(timer);
    if(r.ok){ return res.json({ success:true }); }
    return res.status(200).json({ success:false, status:r.status });
  }catch(e){
    clearTimeout(timer);
    return res.status(200).json({ success:false, error: e && (e.message||String(e)) });
  }
});

/**
 * 健康检查
 */
app.get('/api/health', (req, res) => {
  res.json({
    success: true,
    service: 'PPH Management Interface',
    status: 'running',
    port: PORT,
    timestamp: new Date().toISOString()
  });
});

/**
 * 主页路由
 */
app.get('/', (req, res) => {
  res.sendFile(path.join(__dirname, 'public', 'index.html'));
});

// 启动服务器
app.listen(PORT, '0.0.0.0', () => {
  console.log(`🎛️ PPH Management Interface started`);
  console.log(`📍 Port: ${PORT}`);
  console.log(`🌐 URL: http://localhost:${PORT}`);
  console.log(`📁 Log directory: ${LOG_DIR}`);
  console.log(`⏰ Started at: ${new Date().toISOString()}`);
});

// 优雅关闭
process.on('SIGINT', () => {
  console.log('\n🛑 Shutting down PPH Management Interface...');
  process.exit(0);
});

process.on('SIGTERM', () => {
  console.log('\n🛑 Shutting down PPH Management Interface...');
  process.exit(0);
});
